<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  
  
  <title>WEI BLOG</title>
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  
  
  
    <link rel="alternate" href="/wei-blog/atom.xml" title="WEI BLOG" type="application/atom+xml">
  
  
    <link rel="shortcut icon" href="/wei-blog/favicon.png">
  
  
    
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/typeface-source-code-pro@0.0.71/index.min.css">

  
  
<link rel="stylesheet" href="/wei-blog/css/style.css">

  
  
  
  
<meta name="generator" content="Hexo 6.3.0"></head>

<body>
  <div id="container">
    <div id="wrap">
      <header id="header">
  <div id="header-outer" class="outer">
    <div id="header-title" class="inner">
      <h1 id="logo-wrap">
        <a id="logo">WEI BLOG</a>
      </h1>
      
    </div>
    <div id="header-inner" class="inner">
      <nav id="main-nav">
        
          <a class="main-nav-link" href="/wei-blog/."><i class="fa fa-home"></i> 首页</a>
        
          <a class="main-nav-link" href="/wei-blog/archives/"><i class="fa fa-archive"></i> 归档</a>
        
      </nav>
    </div>
    <div id="search-form">
      <div id="result-mask" class="hide"></div>
      <label><input id="search-key" type="text" autocomplete="off" placeholder="搜索"></label>
      <div id="result-wrap" class="hide">
        <div id="search-result"></div>
      </div>
      <div class="hide">
        <template id="search-tpl">
          <div class="item">
            <a href="/{path}" title="{title}">
              <div class="title">{title}</div>
              <!-- <div class="time">{date}</div> -->
              <div class="tags">{tags}</div>
            </a>
          </div>
        </template>
      </div>
    </div>
  </div>
</header>

      <div class="outer">
        <section id="main">
  <article id="post-使用redis实现浏览数、点赞数、评论数的更新" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/wei-blog/source/%E4%BD%BF%E7%94%A8redis%E5%AE%9E%E7%8E%B0%E6%B5%8F%E8%A7%88%E6%95%B0%E3%80%81%E7%82%B9%E8%B5%9E%E6%95%B0%E3%80%81%E8%AF%84%E8%AE%BA%E6%95%B0%E7%9A%84%E6%9B%B4%E6%96%B0/">使用redis实现浏览数、点赞数、评论数的更新</a>
    </h1>
  


      </header>
    
    <div class="article-meta">
      
      <!-- <span class="article-date">
  <i class="fa fa-date"></i>
  <time class="dt-published" datetime="2023-02-06T09:03:59.000Z" itemprop="datePublished">2023年02月06日</time>
</span> -->
      
  <div class="article-category">
    <i class="fa fa-classify"></i>
    <a class="article-category-link" href="/wei-blog/categories/Redis/">Redis</a>
  </div>

      
      
<!-- <a href="/wei-blog/source/%E4%BD%BF%E7%94%A8redis%E5%AE%9E%E7%8E%B0%E6%B5%8F%E8%A7%88%E6%95%B0%E3%80%81%E7%82%B9%E8%B5%9E%E6%95%B0%E3%80%81%E8%AF%84%E8%AE%BA%E6%95%B0%E7%9A%84%E6%9B%B4%E6%96%B0/#comments" class="article-comment-link">
  
  <i class="fa fa-commt"></i>
  留言
</a> -->


    </div>
    <div class="e-content article-entry" itemprop="articleBody">
      
        <h1 id="执行逻辑"><a href="#执行逻辑" class="headerlink" title="执行逻辑"></a>执行逻辑</h1><p>当获取 post 时，将 post 的各数值信息保存到 Redis 中，定时任务每隔30秒获取一次保存的值，通过 isUpdate 判断该值是否更新过，如更新过则将该值保存到数据库中，如未更新过则根据 updateTimeStamp 判断该值未更新时间间隔，如大于30分钟则删除该键。</p>
<p>RedisKey <code>post:count:&#123;postID&#125;</code></p>
<p>Redis 数据结构</p>
<table>
<thead>
<tr>
<th>field</th>
<th>value</th>
</tr>
</thead>
<tbody><tr>
<td>view</td>
<td>9</td>
</tr>
<tr>
<td>reply</td>
<td>4</td>
</tr>
<tr>
<td>updateTimeStamp</td>
<td>1675674541</td>
</tr>
<tr>
<td>like</td>
<td>3</td>
</tr>
<tr>
<td>isUpdate</td>
<td>1</td>
</tr>
</tbody></table>
<h1 id="逻辑代码"><a href="#逻辑代码" class="headerlink" title="逻辑代码"></a>逻辑代码</h1><h2 id="业务代码"><a href="#业务代码" class="headerlink" title="业务代码"></a>业务代码</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">countRedisKey</span> <span class="operator">=</span> RedisKey.POST_COUNT_PREFIX + postVo.getId();</span><br><span class="line"><span class="comment">// 判断 key 是否已经存在</span></span><br><span class="line"><span class="keyword">if</span> (redisUtil.hasKey(countRedisKey)) &#123;</span><br><span class="line">    <span class="comment">// 已存在则增加浏览量</span></span><br><span class="line">    redisUtil.incrementHashValue(countRedisKey, <span class="string">&quot;view&quot;</span>, <span class="number">1L</span>);</span><br><span class="line">    <span class="comment">// 更新isUpdate和timeStamp</span></span><br><span class="line">    Map&lt;String, Object&gt; updateMap = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;(<span class="number">4</span>);</span><br><span class="line">    updateMap.put(<span class="string">&quot;isUpdate&quot;</span>, <span class="number">1</span>);</span><br><span class="line">    updateMap.put(<span class="string">&quot;updateTimeStamp&quot;</span>, LocalDateTime.now().toEpochSecond(ZoneOffset.of(<span class="string">&quot;+8&quot;</span>)));</span><br><span class="line">    redisUtil.setHashBatch(countRedisKey, updateMap);</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="comment">// 不存在则保存</span></span><br><span class="line">    Map&lt;String, Object&gt; countMap = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;(<span class="number">4</span>);</span><br><span class="line">    countMap.put(<span class="string">&quot;isUpdate&quot;</span>, <span class="number">1</span>);</span><br><span class="line">    countMap.put(<span class="string">&quot;updateTimeStamp&quot;</span>, LocalDateTime.now().toEpochSecond(ZoneOffset.of(<span class="string">&quot;+8&quot;</span>)));</span><br><span class="line">    countMap.put(<span class="string">&quot;view&quot;</span>, postVo.getViewCount() + <span class="number">1</span>);</span><br><span class="line">    countMap.put(<span class="string">&quot;reply&quot;</span>, postVo.getReplyCount());</span><br><span class="line">    countMap.put(<span class="string">&quot;like&quot;</span>, postVo.getLikeCount());</span><br><span class="line">    redisUtil.setHashBatch(countRedisKey, countMap);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="定时任务"><a href="#定时任务" class="headerlink" title="定时任务"></a>定时任务</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@EnableScheduling</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyScheduleTaskConfig</span> &#123;</span><br><span class="line">    <span class="meta">@Resource</span></span><br><span class="line">    <span class="keyword">private</span> RedisUtil redisUtil;</span><br><span class="line">    <span class="meta">@Resource</span></span><br><span class="line">    <span class="keyword">private</span> PostService postService;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * key的有效时间</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">KEY_VALID_SECOND</span> <span class="operator">=</span> <span class="number">30</span> * <span class="number">60</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 帖子浏览量、点赞量、回复量</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Scheduled(initialDelay = 10000, fixedDelay = 70000)</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">postCountScheduleTask</span><span class="params">()</span> &#123;</span><br><span class="line">        log.info(LocalDateTime.now() + <span class="string">&quot; -- 开始将Redis中post:count更新到数据库&quot;</span>);</span><br><span class="line">        Set&lt;String&gt; keys = redisUtil.getKeys(RedisKey.POST_COUNT_PREFIX + <span class="string">&quot;*&quot;</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">if</span> (keys.isEmpty()) &#123;</span><br><span class="line">            log.info(<span class="string">&quot;post:count没有数据，执行结束&quot;</span>);</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        log.info(keys.toString());</span><br><span class="line"></span><br><span class="line">        <span class="type">Post</span> <span class="variable">tempPost</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Post</span>();</span><br><span class="line">        <span class="type">long</span> <span class="variable">currentSecond</span> <span class="operator">=</span> LocalDateTime.now().toEpochSecond(ZoneOffset.of(<span class="string">&quot;+8&quot;</span>));</span><br><span class="line"></span><br><span class="line">        keys.forEach(redisKey -&gt; &#123;</span><br><span class="line">            Map&lt;Object, Object&gt; hashAll = redisUtil.getHashAll(redisKey);</span><br><span class="line"></span><br><span class="line">            log.info(<span class="string">&quot;处理&quot;</span> + redisKey);</span><br><span class="line">            <span class="comment">// 判断是否更新过</span></span><br><span class="line">            <span class="keyword">if</span> ((<span class="type">int</span>) hashAll.get(<span class="string">&quot;isUpdate&quot;</span>) == <span class="number">1</span>) &#123;</span><br><span class="line">                <span class="comment">// 修改redis中isUpdate</span></span><br><span class="line">                redisUtil.setHash(redisKey, <span class="string">&quot;isUpdate&quot;</span>, <span class="number">0</span>);</span><br><span class="line">                <span class="comment">// 保存到数据库</span></span><br><span class="line">                tempPost.setId(Integer.valueOf(redisKey.substring(RedisKey.POST_COUNT_PREFIX.length())));</span><br><span class="line">                tempPost.setLikeCount((Integer) hashAll.get(<span class="string">&quot;like&quot;</span>));</span><br><span class="line">                tempPost.setViewCount((Integer) hashAll.get(<span class="string">&quot;view&quot;</span>));</span><br><span class="line">                tempPost.setReplyCount((Integer) hashAll.get(<span class="string">&quot;reply&quot;</span>));</span><br><span class="line">                log.info(<span class="string">&quot;更新到数据库 --- &quot;</span> + tempPost);</span><br><span class="line">                postService.updateById(tempPost);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="comment">// 判断上次修改时间是否大于30分钟</span></span><br><span class="line">                <span class="keyword">if</span> (currentSecond - (<span class="type">int</span>) hashAll.get(<span class="string">&quot;updateTimeStamp&quot;</span>) &gt;= KEY_VALID_SECOND) &#123;</span><br><span class="line">                    <span class="comment">// 大于30分钟则删除key</span></span><br><span class="line">                    log.info(redisKey + <span class="string">&quot; --- 已过期删除&quot;</span>);</span><br><span class="line">                    redisUtil.delete(redisKey);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">        log.info(LocalDateTime.now() + <span class="string">&quot; -- 将Redis中post:count更新到数据库执行结束&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>


        
        
      
    </div>
    <footer class="article-footer">
      
        <div class="article-tag-wrap">
          

          
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/wei-blog/tags/%E9%80%BB%E8%BE%91/" rel="tag">逻辑</a></li></ul>

          


        </div>
      
      <!--  -->
      <!--  -->
    </footer>
  </div>
</article>


  <article id="post-RabbitMQ使用延迟队列" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-inner">
    
    
    <div class="article-meta">
      
      <!-- <span class="article-date">
  <i class="fa fa-date"></i>
  <time class="dt-published" datetime="2023-02-06T07:12:34.000Z" itemprop="datePublished">2023年02月06日</time>
</span> -->
      
  <div class="article-category">
    <i class="fa fa-classify"></i>
    <a class="article-category-link" href="/wei-blog/categories/RabbitMQ/">RabbitMQ</a>
  </div>

      
      
<!-- <a href="/wei-blog/source/RabbitMQ%E4%BD%BF%E7%94%A8%E5%BB%B6%E8%BF%9F%E9%98%9F%E5%88%97/#comments" class="article-comment-link">
  
  <i class="fa fa-commt"></i>
  留言
</a> -->


    </div>
    <div class="e-content article-entry" itemprop="articleBody">
      
        <blockquote>
<p>通过 x-delayed-message 声明的交换机，它的消息在发布之后不会立即进入队列，先将消息保存至 Mnesia（一个分布式数据库管理系统）。这个插件将会尝试确认消息是否过期，首先要确保消息的延迟范围是 Delay &gt; 0, Delay &#x3D;&lt; ?ERL_MAX_T（在Erlang 中可以被设置的范围(2^32)-1 毫秒），如果消息过期通过 x-delayed-type 类型标记的交换机投递至目标队列，整个消息的投递过程也就完成了。</p>
</blockquote>
<h1 id="安装插件"><a href="#安装插件" class="headerlink" title="安装插件"></a>安装插件</h1><p>插件地址：<a target="_blank" rel="noopener" href="https://github.com/rabbitmq/rabbitmq-delayed-message-exchange">https://github.com/rabbitmq/rabbitmq-delayed-message-exchange</a></p>
<p><strong>安装完后需要重启服务</strong></p>
<p>如下安装成功</p>
<p><img src="https://gitee.com/xueweitao/images/raw/master/RabbitMQ%E4%BD%BF%E7%94%A8%E5%BB%B6%E8%BF%9F%E9%98%9F%E5%88%97-%E5%AE%89%E8%A3%85-2023-2-917:13:10.png"></p>
<h1 id="声明交换机和队列"><a href="#声明交换机和队列" class="headerlink" title="声明交换机和队列"></a>声明交换机和队列</h1><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RabbitDelayConfig</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">DELAY_MSG_QUEUE</span> <span class="operator">=</span> <span class="string">&quot;delay_msg_queue&quot;</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">DELAY_MSG_EXCHANGE</span> <span class="operator">=</span> <span class="string">&quot;delay_msg_exchange&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> Queue <span class="title function_">delayQueue</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Queue</span>(DELAY_MSG_QUEUE, <span class="literal">true</span>, <span class="literal">false</span>, <span class="literal">false</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> CustomExchange <span class="title function_">delayExchange</span><span class="params">()</span> &#123;</span><br><span class="line">        HashMap&lt;String, Object&gt; args = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">        <span class="comment">// 延迟队列类型</span></span><br><span class="line">        args.put(<span class="string">&quot;x-delayed-type&quot;</span>, <span class="string">&quot;direct&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">CustomExchange</span>(DELAY_MSG_EXCHANGE, <span class="string">&quot;x-delayed-message&quot;</span>, <span class="literal">true</span>, <span class="literal">false</span>, args);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> Binding <span class="title function_">delayBinding</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> BindingBuilder.bind(delayQueue()).to(delayExchange()).with(DELAY_MSG_QUEUE).noargs();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h1 id="发送延迟消息"><a href="#发送延迟消息" class="headerlink" title="发送延迟消息"></a>发送延迟消息</h1><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Message</span> <span class="variable">message</span> <span class="operator">=</span> MessageBuilder</span><br><span class="line">        .withBody(LocalDateTime.now().toString().getBytes(StandardCharsets.UTF_8))</span><br><span class="line">    	<span class="comment">// 设置延迟时间 毫秒</span></span><br><span class="line">        .setHeader(<span class="string">&quot;x-delay&quot;</span>, <span class="number">10000</span>)</span><br><span class="line">        .build();</span><br><span class="line"></span><br><span class="line">rabbitTemplate.send(RabbitDelayConfig.DELAY_MSG_EXCHANGE, RabbitDelayConfig.DELAY_MSG_QUEUE, message);</span><br></pre></td></tr></table></figure>

<h1 id="监听队列"><a href="#监听队列" class="headerlink" title="监听队列"></a>监听队列</h1><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DelayMsgListener</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@RabbitListener(queues = RabbitDelayConfig.DELAY_MSG_QUEUE)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">handleMsg</span><span class="params">(String msg)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;msg = &quot;</span> + msg);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

        
        
      
    </div>
    <footer class="article-footer">
      
        <div class="article-tag-wrap">
          

          
          


        </div>
      
      <!--  -->
      <!--  -->
    </footer>
  </div>
</article>


  <article id="post-SpringBoot使用定时任务" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/wei-blog/source/SpringBoot%E4%BD%BF%E7%94%A8%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1/">SpringBoot定时任务</a>
    </h1>
  


      </header>
    
    <div class="article-meta">
      
      <!-- <span class="article-date">
  <i class="fa fa-date"></i>
  <time class="dt-published" datetime="2023-02-06T07:12:34.000Z" itemprop="datePublished">2023年02月06日</time>
</span> -->
      
  <div class="article-category">
    <i class="fa fa-classify"></i>
    <a class="article-category-link" href="/wei-blog/categories/SpringBoot/">SpringBoot</a>
  </div>

      
      
<!-- <a href="/wei-blog/source/SpringBoot%E4%BD%BF%E7%94%A8%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1/#comments" class="article-comment-link">
  
  <i class="fa fa-commt"></i>
  留言
</a> -->


    </div>
    <div class="e-content article-entry" itemprop="articleBody">
      
        <h1 id="基于注解的定时任务"><a href="#基于注解的定时任务" class="headerlink" title="基于注解的定时任务"></a>基于注解的定时任务</h1><blockquote>
<p>基于注解@Scheduled默认为单线程，开启多个任务时，任务的执行时机会受上一个任务执行时间的影响</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@EnableScheduling</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyScheduleTaskConfig</span> &#123;</span><br><span class="line">	</span><br><span class="line">    <span class="comment">// 每隔30秒执行一次</span></span><br><span class="line">    <span class="comment">//    @Scheduled(fixedRate = 30000)</span></span><br><span class="line">    <span class="meta">@Scheduled(cron = &quot;0/30 * * * * ?&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">postCountScheduleTask</span><span class="params">()</span> &#123;</span><br><span class="line">        log.info(LocalDateTime.now() + <span class="string">&quot; -- 定时任务开始执行&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>corn表达式工具 <a target="_blank" rel="noopener" href="https://cron.qqe2.com/">https://cron.qqe2.com/</a></p>
<p>fixedDelay</p>
<p>​	当一个任务执行完毕后等待固定的时间间隔再开始执行下一个任务</p>
<p>fixedRate</p>
<p>​	不管前一个任务是否执行完毕，总是匀速执行任务</p>
<p>initialDelay</p>
<p>​	<code>@Scheduled(initialDelay = 10000, fixedRate = 15000)</code></p>
<p>​	这个定时器就是在上一个的基础上加了一个initialDelay &#x3D; 10000 意思就是在容器启动后,延迟10秒后再执行一次定时器,</p>
<p>以后每15秒再执行一次该定时器。</p>
<h1 id="多线程定时任务"><a href="#多线程定时任务" class="headerlink" title="多线程定时任务"></a>多线程定时任务</h1><blockquote>
<p>每个不同的任务运行互不干扰</p>
<p>相同的任务运行时也互不干扰，会出现某个定时任务执行过程中又开始该任务的定时任务造成重叠</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="meta">@EnableScheduling</span></span><br><span class="line"><span class="meta">@EnableAsync</span> <span class="comment">// 开启多线程</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MultiThreadScheduleTaskConfig</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Async</span></span><br><span class="line">    <span class="meta">@Scheduled(fixedDelay = 1000)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">first</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;第一个定时任务开始 : &quot;</span> + LocalDateTime.now().toLocalTime() + <span class="string">&quot;\r\n线程 : &quot;</span> + Thread.currentThread().getName());</span><br><span class="line">        Thread.sleep(<span class="number">1000</span> * <span class="number">10</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Async</span></span><br><span class="line">    <span class="meta">@Scheduled(fixedDelay = 2000)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">second</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;第二个定时任务开始 : &quot;</span> + LocalDateTime.now().toLocalTime() + <span class="string">&quot;\r\n线程 : &quot;</span> + Thread.currentThread().getName());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
        
        
      
    </div>
    <footer class="article-footer">
      
        <div class="article-tag-wrap">
          

          
          


        </div>
      
      <!--  -->
      <!--  -->
    </footer>
  </div>
</article>


  <article id="post-LocalDateTime的使用" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/wei-blog/source/LocalDateTime%E7%9A%84%E4%BD%BF%E7%94%A8/">LocalDateTime的使用</a>
    </h1>
  


      </header>
    
    <div class="article-meta">
      
      <!-- <span class="article-date">
  <i class="fa fa-date"></i>
  <time class="dt-published" datetime="2023-02-06T07:04:34.000Z" itemprop="datePublished">2023年02月06日</time>
</span> -->
      
  <div class="article-category">
    <i class="fa fa-classify"></i>
    <a class="article-category-link" href="/wei-blog/categories/Java/">Java</a>
  </div>

      
      
<!-- <a href="/wei-blog/source/LocalDateTime%E7%9A%84%E4%BD%BF%E7%94%A8/#comments" class="article-comment-link">
  
  <i class="fa fa-commt"></i>
  留言
</a> -->


    </div>
    <div class="e-content article-entry" itemprop="articleBody">
      
        <blockquote>
<p>LocalDateTime 同时含有年月日时分秒的日期对象</p>
</blockquote>
<h1 id="获取时间戳"><a href="#获取时间戳" class="headerlink" title="获取时间戳"></a>获取时间戳</h1><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//获取秒数</span></span><br><span class="line">System.out.println(LocalDateTime.now().toEpochSecond(ZoneOffset.of(<span class="string">&quot;+8&quot;</span>)));</span><br><span class="line"></span><br><span class="line"><span class="comment">//获取毫秒数</span></span><br><span class="line">System.out.println(LocalDateTime.now().toInstant(ZoneOffset.of(<span class="string">&quot;+8&quot;</span>)).toEpochMilli());</span><br></pre></td></tr></table></figure>



<h1 id="日期格式化"><a href="#日期格式化" class="headerlink" title="日期格式化"></a>日期格式化</h1><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">DateTimeFormatter</span> <span class="variable">formatter</span> <span class="operator">=</span> DateTimeFormatter.ofPattern(<span class="string">&quot;yyyyMMddHHmmssSSS&quot;</span>);</span><br><span class="line"><span class="type">String</span> <span class="variable">dateTime</span> <span class="operator">=</span> LocalDateTime.now(ZoneOffset.of(<span class="string">&quot;+8&quot;</span>)).format(formatter);</span><br></pre></td></tr></table></figure>



<h1 id="字符串转换"><a href="#字符串转换" class="headerlink" title="字符串转换"></a>字符串转换</h1><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">dateTimeStr</span> <span class="operator">=</span> <span class="string">&quot;2023-02-26 14:11:15&quot;</span>;</span><br><span class="line"><span class="type">DateTimeFormatter</span> <span class="variable">df</span> <span class="operator">=</span> DateTimeFormatter.ofPattern(<span class="string">&quot;yyyy-MM-dd HH:mm:ss&quot;</span>);</span><br><span class="line"><span class="type">LocalDateTime</span> <span class="variable">dateTime</span> <span class="operator">=</span> LocalDateTime.parse(dateTimeStr, df);</span><br></pre></td></tr></table></figure>


        
        
      
    </div>
    <footer class="article-footer">
      
        <div class="article-tag-wrap">
          

          
          


        </div>
      
      <!--  -->
      <!--  -->
    </footer>
  </div>
</article>


  <article id="post-Redis之Hash" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/wei-blog/source/Redis%E4%B9%8BHash/">Redis之Hash</a>
    </h1>
  


      </header>
    
    <div class="article-meta">
      
      <!-- <span class="article-date">
  <i class="fa fa-date"></i>
  <time class="dt-published" datetime="2023-02-06T05:44:34.000Z" itemprop="datePublished">2023年02月06日</time>
</span> -->
      
  <div class="article-category">
    <i class="fa fa-classify"></i>
    <a class="article-category-link" href="/wei-blog/categories/Redis/">Redis</a>
  </div>

      
      
<!-- <a href="/wei-blog/source/Redis%E4%B9%8BHash/#comments" class="article-comment-link">
  
  <i class="fa fa-commt"></i>
  留言
</a> -->


    </div>
    <div class="e-content article-entry" itemprop="articleBody">
      
        <h1 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h1><p>Redis hash 是一个String类型的field 和value 的映射表，hash特别适合用于存储对象类似于Java里面的Map&lt;String,Object&gt;</p>
<h1 id="操作"><a href="#操作" class="headerlink" title="操作"></a>操作</h1><blockquote>
<p>给 key 集合中的 field 键赋值 value</p>
<p><code>hset key field value</code></p>
</blockquote>
<blockquote>
<p>从 key 中取出指定的 field 对应的 value</p>
<p><code>hget key field</code></p>
</blockquote>
<blockquote>
<p>当指定 key 中 field 不存在时存入 value<br><code>hsetnx key field value</code></p>
</blockquote>
<blockquote>
<p>批量插入 key 的 field-value 对</p>
<p><code>hmset key field01 value01 field02 value02 ...</code></p>
</blockquote>
<blockquote>
<p>批量获取 key 的 field 值</p>
<p><code>hmget key field01 field02 ...</code></p>
</blockquote>
<blockquote>
<p>查看指定 key 的哈希表中是否存在指定 field</p>
<p><code>hexists key field</code></p>
</blockquote>
<blockquote>
<p>获取指定 key 中所有 field</p>
<p><code>hkeys key</code></p>
</blockquote>
<blockquote>
<p>获取指定 key 中所有 value</p>
<p><code>hvals key</code></p>
</blockquote>
<blockquote>
<p>获取指定 key 中所有 field 和 value</p>
<p><code>hgetall key</code></p>
</blockquote>
<blockquote>
<p>删除指定 key 中 field</p>
<p><code>hdel key field01 field02 ...</code></p>
</blockquote>
<blockquote>
<p>获取 key 中 field 数量</p>
<p><code>hlen key</code></p>
</blockquote>
<blockquote>
<p>为 key 中 对应的 field 的 value 加上指定增量值 increment</p>
<p><code>hincrby key field increment</code></p>
<p>增量值也可为负</p>
<p>如果指定的字段不存在，会先初始化为0</p>
<p>如果该字段对应的 value 值存储的为字符串值，那么该命令会执行出错</p>
</blockquote>

        
        
      
    </div>
    <footer class="article-footer">
      
        <div class="article-tag-wrap">
          

          
          


        </div>
      
      <!--  -->
      <!--  -->
    </footer>
  </div>
</article>


  <article id="post-Redis基本介绍" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/wei-blog/source/Redis%E5%9F%BA%E6%9C%AC%E4%BB%8B%E7%BB%8D/">Redis基本介绍</a>
    </h1>
  


      </header>
    
    <div class="article-meta">
      
      <!-- <span class="article-date">
  <i class="fa fa-date"></i>
  <time class="dt-published" datetime="2023-02-05T16:01:34.000Z" itemprop="datePublished">2023年02月06日</time>
</span> -->
      
  <div class="article-category">
    <i class="fa fa-classify"></i>
    <a class="article-category-link" href="/wei-blog/categories/Redis/">Redis</a>
  </div>

      
      
<!-- <a href="/wei-blog/source/Redis%E5%9F%BA%E6%9C%AC%E4%BB%8B%E7%BB%8D/#comments" class="article-comment-link">
  
  <i class="fa fa-commt"></i>
  留言
</a> -->


    </div>
    <div class="e-content article-entry" itemprop="articleBody">
      
        <h1 id="规范"><a href="#规范" class="headerlink" title="规范"></a>规范</h1><p>以英文字母开头，命名中只能出现小写字母、数字、英文点号 <code>.</code> 和英文半角冒号 <code>:</code> </p>
<p>命名规范：业务模块名:业务逻辑含义:其他:value类型</p>
<p>Redis 中 key 的命名，用 <code>:</code> 分隔不同的层次命名空间。如 <code>user:12345:contact</code></p>
<p>如果某个对象中的字段，用 <code>.</code> 连接。如 <code>user:12345:contact.mail</code></p>
<p>String类型控制在10KB以内，Hash、List、Set、ZSet元素个数不要超过5000</p>
<p>Redis key 一定要设置过期时间。要跟自己的业务场景，需要对 key 设置合理的过期时间。可以在写入 key 时，就要追加过期时间；也可以在需要写入另一个 key 时，删除上一个 key</p>
<h1 id="通用操作"><a href="#通用操作" class="headerlink" title="通用操作"></a>通用操作</h1><blockquote>
<p>获取符合规则的键名列表 </p>
<p><code>keys pattern</code></p>
<p>pattern 类型</p>
<p>​	<code>?</code> 匹配一个字符</p>
<p>​	<code>*</code> 匹配任意个字符</p>
<p>​	<code>[]</code> 匹配括号内任一字符</p>
<p>​	<code>/</code> 转移符号</p>
</blockquote>
<blockquote>
<p>判断一个 key 是否存在 </p>
<p><code>exists key</code></p>
</blockquote>
<blockquote>
<p>删除键 </p>
<p><code>del key...</code></p>
<p>可以删除一个或多个键</p>
</blockquote>
<blockquote>
<p>判断 key 的数据类型 </p>
<p><code>type key</code></p>
</blockquote>
<blockquote>
<p>查询 key 的有效时间 </p>
<p><code>ttl key</code> 以秒为单位返回  </p>
<p><code>pttl key</code> 以毫秒为单位返回</p>
</blockquote>
<blockquote>
<p>删除 key 的失效时间</p>
<p><code>persist key</code></p>
</blockquote>
<blockquote>
<p>设置 key 的过期时间</p>
<p><code>expire key second</code></p>
<p><code>pexpire key milliseconds</code></p>
<p><code>expireat key timestamp</code></p>
<p><code>pexpireat key milliseconds-timestamp</code></p>
</blockquote>

        
        
      
    </div>
    <footer class="article-footer">
      
        <div class="article-tag-wrap">
          

          
          


        </div>
      
      <!--  -->
      <!--  -->
    </footer>
  </div>
</article>


  <article id="post-Redis之String" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/wei-blog/source/Redis%E4%B9%8BString/">Redis之String</a>
    </h1>
  


      </header>
    
    <div class="article-meta">
      
      <!-- <span class="article-date">
  <i class="fa fa-date"></i>
  <time class="dt-published" datetime="2023-02-05T15:26:34.000Z" itemprop="datePublished">2023年02月05日</time>
</span> -->
      
  <div class="article-category">
    <i class="fa fa-classify"></i>
    <a class="article-category-link" href="/wei-blog/categories/Redis/">Redis</a>
  </div>

      
      
<!-- <a href="/wei-blog/source/Redis%E4%B9%8BString/#comments" class="article-comment-link">
  
  <i class="fa fa-commt"></i>
  留言
</a> -->


    </div>
    <div class="e-content article-entry" itemprop="articleBody">
      
        <h1 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h1><p>String 类型是二进制安全的，意思是 Redis 的 String 可以包含任何数据，比如 jpg 图片或者序列化的对象。</p>
<p>String 类型是 Redis 最基本的数据类型，一个 Redis 中字符串 value 最多可以是 512M</p>
<h1 id="操作"><a href="#操作" class="headerlink" title="操作"></a>操作</h1><blockquote>
<p>存入值</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">&gt; set name xwt</span><br><span class="line">OK</span><br></pre></td></tr></table></figure>



<blockquote>
<p>获取值</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">&gt; get name</span><br><span class="line">xwt</span><br></pre></td></tr></table></figure>



<blockquote>
<p>追加数据</p>
</blockquote>
<p>对不存在的 key 进行 append 操作相当于 set</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">&gt; append key00 value00</span><br><span class="line"><span class="number">7</span></span><br><span class="line">&gt; append key01 value01</span><br><span class="line"><span class="number">14</span></span><br><span class="line">&gt; get key00</span><br><span class="line">value00</span><br><span class="line">&gt; get key01</span><br><span class="line">value01value01</span><br></pre></td></tr></table></figure>



<blockquote>
<p>获取 key 对应的字符串长度</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">&gt; strlen key00</span><br><span class="line"><span class="number">7</span></span><br></pre></td></tr></table></figure>



<blockquote>
<p>增加、减少 key 的值</p>
</blockquote>
<p>只有当 value 的数据类型为 number 时才能使用</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">&gt; set count <span class="number">0</span></span><br><span class="line">OK</span><br><span class="line">&gt; incr count</span><br><span class="line"><span class="number">1</span></span><br><span class="line">&gt; decr count</span><br><span class="line"><span class="number">0</span></span><br><span class="line">&gt; incr key00</span><br><span class="line">ERR value is not an integer or out of range</span><br></pre></td></tr></table></figure>



<blockquote>
<p>指定增加、减少 key 的值</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&gt; incrby count <span class="number">10</span></span><br><span class="line"><span class="number">30</span></span><br><span class="line">&gt; decrby count <span class="number">20</span></span><br><span class="line"><span class="number">10</span></span><br></pre></td></tr></table></figure>



<blockquote>
<p>获取 key 指定区间范围内的值</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">&gt; getrange count <span class="number">0</span> -<span class="number">1</span></span><br><span class="line"><span class="number">10</span></span><br><span class="line">&gt; getrange count <span class="number">0</span> <span class="number">0</span></span><br><span class="line"><span class="number">1</span></span><br><span class="line">&gt; getrange count <span class="number">0</span> <span class="number">1</span></span><br><span class="line"><span class="number">10</span></span><br><span class="line">&gt; getrange key00 <span class="number">0</span> <span class="number">5</span></span><br><span class="line">value0</span><br></pre></td></tr></table></figure>



<blockquote>
<p>设置 key 指定区间范围内的值</p>
</blockquote>
<p>从指定的下标开始替换</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&gt; get key00</span><br><span class="line">value00</span><br><span class="line">&gt; setrange key00 <span class="number">1</span> b</span><br><span class="line"><span class="number">7</span></span><br><span class="line">&gt; get key00</span><br><span class="line">vblue00</span><br></pre></td></tr></table></figure>



<blockquote>
<p>不存在值则添加</p>
</blockquote>
<p>添加成功返回 1 ，值存在返回 0</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&gt; setnx key00 value00</span><br><span class="line"><span class="number">0</span></span><br><span class="line">&gt; setnx key03 value03</span><br><span class="line"><span class="number">1</span></span><br></pre></td></tr></table></figure>



<blockquote>
<p>设置带有过期时间的 key</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&gt; setex key04 <span class="number">10</span> value04</span><br><span class="line">OK</span><br><span class="line">&gt; ttl key04</span><br><span class="line"><span class="number">4</span></span><br></pre></td></tr></table></figure>



<blockquote>
<p>批量操作</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">mset key1 value1 ... keyn valuen：添加多个键值对。</span><br><span class="line">mget key1 value1 ... keyn valuen：查询多个键所对应的值。</span><br><span class="line">msetnx key1 value1 ... keyn valuen：添加多个键值对，只有键不存在，才可以添加成功。</span><br></pre></td></tr></table></figure>



<blockquote>
<p>新值替换旧值</p>
</blockquote>
<p>存在则替换，不存在则新增</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&gt; getset key00 newvalue00</span><br><span class="line">vblue00</span><br><span class="line">&gt; get key00</span><br><span class="line">newvalue00</span><br></pre></td></tr></table></figure>


        
        
      
    </div>
    <footer class="article-footer">
      
        <div class="article-tag-wrap">
          

          
          


        </div>
      
      <!--  -->
      <!--  -->
    </footer>
  </div>
</article>


  <article id="post-Redis持久化" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/wei-blog/source/Redis%E6%8C%81%E4%B9%85%E5%8C%96/">Redis持久化</a>
    </h1>
  


      </header>
    
    <div class="article-meta">
      
      <!-- <span class="article-date">
  <i class="fa fa-date"></i>
  <time class="dt-published" datetime="2023-02-05T13:16:34.000Z" itemprop="datePublished">2023年02月05日</time>
</span> -->
      
  <div class="article-category">
    <i class="fa fa-classify"></i>
    <a class="article-category-link" href="/wei-blog/categories/Redis/">Redis</a>
  </div>

      
      
<!-- <a href="/wei-blog/source/Redis%E6%8C%81%E4%B9%85%E5%8C%96/#comments" class="article-comment-link">
  
  <i class="fa fa-commt"></i>
  留言
</a> -->


    </div>
    <div class="e-content article-entry" itemprop="articleBody">
      
        <h1 id="RDB"><a href="#RDB" class="headerlink" title="RDB"></a>RDB</h1><blockquote>
<p>RDB持久化方式能够在指定的时间间隔能对你的数据进行快照存储</p>
</blockquote>
<h2 id="配置文件"><a href="#配置文件" class="headerlink" title="配置文件"></a>配置文件</h2><figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">################################ SNAPSHOTTING  ################################</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># Save the DB on disk:</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment">#   save &lt;seconds&gt; &lt;changes&gt;</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment">#   Will save the DB if both the given number of seconds and the given</span></span><br><span class="line"><span class="comment">#   number of write operations against the DB occurred.</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment">#   In the example below the behaviour will be to save:</span></span><br><span class="line"><span class="comment">#   after 900 sec (15 min) if at least 1 key changed</span></span><br><span class="line"><span class="comment">#   after 300 sec (5 min) if at least 10 keys changed</span></span><br><span class="line"><span class="comment">#   after 60 sec if at least 10000 keys changed</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment">#   <span class="doctag">Note:</span> you can disable saving completely by commenting out all &quot;save&quot; lines.</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment">#   It is also possible to remove all the previously configured save</span></span><br><span class="line"><span class="comment">#   points by adding a save directive with a single empty string argument</span></span><br><span class="line"><span class="comment">#   like in the following example:</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment">#   save &quot;&quot;</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">#只要满足以下三个条件中的任意一个，BGSAVE命令就会被执行</span></span><br><span class="line"><span class="comment">#服务器在900秒之内，对数据库进行了至少1次修改</span></span><br><span class="line"><span class="comment">#服务器在300秒之内，对数据库进行了至少10次修改</span></span><br><span class="line"><span class="comment">#服务器在60秒之内，对数据库进行了至少10000次修改</span></span><br><span class="line"></span><br><span class="line"><span class="attr">save</span> <span class="string">900 1</span></span><br><span class="line"><span class="attr">save</span> <span class="string">300 10</span></span><br><span class="line"><span class="attr">save</span> <span class="string">60 10000</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># By default Redis will stop accepting writes if RDB snapshots are enabled</span></span><br><span class="line"><span class="comment"># (at least one save point) and the latest background save failed.</span></span><br><span class="line"><span class="comment"># This will make the user aware (in a hard way) that data is not persisting</span></span><br><span class="line"><span class="comment"># on disk properly, otherwise chances are that no one will notice and some</span></span><br><span class="line"><span class="comment"># disaster will happen.</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># If the background saving process will start working again Redis will</span></span><br><span class="line"><span class="comment"># automatically allow writes again.</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># However if you have setup your proper monitoring of the Redis server</span></span><br><span class="line"><span class="comment"># and persistence, you may want to disable this feature so that Redis will</span></span><br><span class="line"><span class="comment"># continue to work as usual even if there are problems with disk,</span></span><br><span class="line"><span class="comment"># permissions, and so forth.</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># 后台保存失败时，Redis是否停止接受写入</span></span><br><span class="line"><span class="attr">stop-writes-on-bgsave-error</span> <span class="string">yes</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># Compress string objects using LZF when dump .rdb databases?</span></span><br><span class="line"><span class="comment"># For default that&#x27;s set to &#x27;yes&#x27; as it&#x27;s almost always a win.</span></span><br><span class="line"><span class="comment"># If you want to save some CPU in the saving child set it to &#x27;no&#x27; but</span></span><br><span class="line"><span class="comment"># the dataset will likely be bigger if you have compressible values or keys.</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># 是否压缩RDB文件，需要占用CPU资源</span></span><br><span class="line"><span class="attr">rdbcompression</span> <span class="string">yes</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># Since version 5 of RDB a CRC64 checksum is placed at the end of the file.</span></span><br><span class="line"><span class="comment"># This makes the format more resistant to corruption but there is a performance</span></span><br><span class="line"><span class="comment"># hit to pay (around 10%) when saving and loading RDB files, so you can disable it</span></span><br><span class="line"><span class="comment"># for maximum performances.</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># RDB files created with checksum disabled have a checksum of zero that will</span></span><br><span class="line"><span class="comment"># tell the loading code to skip the check.</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># 存储快照后，使用CRC64算法来进行数据校验，需要多花费大约10%的性能</span></span><br><span class="line"><span class="attr">rdbchecksum</span> <span class="string">yes</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># The filename where to dump the DB</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># 生成的文件名称</span></span><br><span class="line"><span class="attr">dbfilename</span> <span class="string">dump.rdb</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># The working directory.</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># The DB will be written inside this directory, with the filename specified</span></span><br><span class="line"><span class="comment"># above using the &#x27;dbfilename&#x27; configuration directive.</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># The Append Only File will also be created inside this directory.</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># Note that you must specify a directory here, not a file name.</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># 生成的文件位置</span></span><br><span class="line"><span class="attr">dir</span> <span class="string">./</span></span><br></pre></td></tr></table></figure>

<h2 id="基本原理"><a href="#基本原理" class="headerlink" title="基本原理"></a>基本原理</h2><ul>
<li><p>Redis 启动后会读取 RDB 快照文件，将数据从硬盘载入到内存，一般情况下 1GB 的快照文件载入到内存的时间大约20-30 秒钟</p>
</li>
<li><p>Redis 需要执行 RDB 的时候，服务器会执行以下操作</p>
<ul>
<li>Redis 调用系统函数 <code>fork()</code> ，创建一个子进程进行持久化，新的 RDB 文件存储的是执行 <code>fork()</code> 那一刻的内存数据</li>
<li>子进程将数据集写入到一个临时 RDB 文件中，也就是将内存中的数据写入临时文件</li>
<li>当子进程完成对临时 RDB 文件的写入时，Redis 用新的临时 RDB 文件替换原来的 RDB 文件，并删除旧 RDB 文件</li>
</ul>
</li>
<li><p>Redis 在进行快照的过程中不会修改 RDB 文件，只有快照结束后才会将旧的文件替换成新的，也就是说任何时候 RDB 文件都是完整的。RDB 文件是经过压缩（可以配置 rdbcompression 参数以禁用压缩节省 CPU 占用）的二进制格式，所以占用的空间会小于内存中的数据大小，更加利于传输。</p>
</li>
<li><p>除了自动快照，还可以手动发送 <code>SAVE</code> 或 <code>BGSAVE</code> 命令让 Redis 执行快照，两个命令的区别在于，前者是由主进程进行快照操作，会阻塞住其他请求，后者会通过 fork 子进程进行快照操作。 </p>
</li>
<li><p>通过RDB方式实现持久化，一旦Redis异常退出，就会丢失最后一次快照以后更改的所有数据。</p>
</li>
</ul>
<h1 id="AOF"><a href="#AOF" class="headerlink" title="AOF"></a>AOF</h1><blockquote>
<p>AOF持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据</p>
</blockquote>
<h2 id="配置文件-1"><a href="#配置文件-1" class="headerlink" title="配置文件"></a>配置文件</h2><figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">############################## APPEND ONLY MODE ###############################</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># By default Redis asynchronously dumps the dataset on disk. This mode is</span></span><br><span class="line"><span class="comment"># good enough in many applications, but an issue with the Redis process or</span></span><br><span class="line"><span class="comment"># a power outage may result into a few minutes of writes lost (depending on</span></span><br><span class="line"><span class="comment"># the configured save points).</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># The Append Only File is an alternative persistence mode that provides</span></span><br><span class="line"><span class="comment"># much better durability. For instance using the default data fsync policy</span></span><br><span class="line"><span class="comment"># (see later in the config file) Redis can lose just one second of writes in a</span></span><br><span class="line"><span class="comment"># dramatic event like a server power outage, or a single write if something</span></span><br><span class="line"><span class="comment"># wrong with the Redis process itself happens, but the operating system is</span></span><br><span class="line"><span class="comment"># still running correctly.</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># AOF and RDB persistence can be enabled at the same time without problems.</span></span><br><span class="line"><span class="comment"># If the AOF is enabled on startup Redis will load the AOF, that is the file</span></span><br><span class="line"><span class="comment"># with the better durability guarantees.</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># Please check http://redis.io/topics/persistence for more information.</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># 是否开启AOF</span></span><br><span class="line"><span class="attr">appendonly</span> <span class="string">no</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># The name of the append only file (default: &quot;appendonly.aof&quot;)</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># AOF备份文件的名称</span></span><br><span class="line"><span class="attr">appendfilename</span> <span class="string">&quot;appendonly.aof&quot;</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># The fsync() call tells the Operating System to actually write data on disk</span></span><br><span class="line"><span class="comment"># instead of waiting for more data in the output buffer. Some OS will really flush</span></span><br><span class="line"><span class="comment"># data on disk, some other OS will just try to do it ASAP.</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># Redis supports three different modes:</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># no: don&#x27;t fsync, just let the OS flush the data when it wants. Faster.</span></span><br><span class="line"><span class="comment"># always: fsync after every write to the append only log. Slow, Safest.</span></span><br><span class="line"><span class="comment"># everysec: fsync only one time every second. Compromise.</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># The default is &quot;everysec&quot;, as that&#x27;s usually the right compromise between</span></span><br><span class="line"><span class="comment"># speed and data safety. It&#x27;s up to you to understand if you can relax this to</span></span><br><span class="line"><span class="comment"># &quot;no&quot; that will let the operating system flush the output buffer when</span></span><br><span class="line"><span class="comment"># it wants, for better performances (but if you can live with the idea of</span></span><br><span class="line"><span class="comment"># some data loss consider the default persistence mode that&#x27;s snapshotting),</span></span><br><span class="line"><span class="comment"># or on the contrary, use &quot;always&quot; that&#x27;s very slow but a bit safer than</span></span><br><span class="line"><span class="comment"># everysec.</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># More details please check the following article:</span></span><br><span class="line"><span class="comment"># http://antirez.com/post/redis-persistence-demystified.html</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># If unsure, use &quot;everysec&quot;.</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># 磁盘同步策略</span></span><br><span class="line"><span class="comment"># appendfsync always 每次执行操作后立即保存到磁盘</span></span><br><span class="line"><span class="comment"># appendfsync everysec 每秒一次</span></span><br><span class="line"><span class="comment"># appendfsync no 由操作系统执行，默认Linux配置最多丢失30秒</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># appendfsync always</span></span><br><span class="line"><span class="attr">appendfsync</span> <span class="string">everysec</span></span><br><span class="line"><span class="comment"># appendfsync no</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># When the AOF fsync policy is set to always or everysec, and a background</span></span><br><span class="line"><span class="comment"># saving process (a background save or AOF log background rewriting) is</span></span><br><span class="line"><span class="comment"># performing a lot of I/O against the disk, in some Linux configurations</span></span><br><span class="line"><span class="comment"># Redis may block too long on the fsync() call. Note that there is no fix for</span></span><br><span class="line"><span class="comment"># this currently, as even performing fsync in a different thread will block</span></span><br><span class="line"><span class="comment"># our synchronous write(2) call.</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># In order to mitigate this problem it&#x27;s possible to use the following option</span></span><br><span class="line"><span class="comment"># that will prevent fsync() from being called in the main process while a</span></span><br><span class="line"><span class="comment"># BGSAVE or BGREWRITEAOF is in progress.</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># This means that while another child is saving, the durability of Redis is</span></span><br><span class="line"><span class="comment"># the same as &quot;appendfsync none&quot;. In practical terms, this means that it is</span></span><br><span class="line"><span class="comment"># possible to lose up to 30 seconds of log in the worst scenario (with the</span></span><br><span class="line"><span class="comment"># default Linux settings).</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># If you have latency problems turn this to &quot;yes&quot;. Otherwise leave it as</span></span><br><span class="line"><span class="comment"># &quot;no&quot; that is the safest pick from the point of view of durability.</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># 当后台执行 BGSAVE 或 AOF 重写时是否停止保存AOF</span></span><br><span class="line"><span class="comment"># no表示新的主进程的set操作会被阻塞掉</span></span><br><span class="line"><span class="comment"># yes表示新的主进程的set不会被阻塞，待整个后台写完成之后再将这部分set操作同步到aof文件中</span></span><br><span class="line"><span class="attr">no-appendfsync-on-rewrite</span> <span class="string">no</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># Automatic rewrite of the append only file.</span></span><br><span class="line"><span class="comment"># Redis is able to automatically rewrite the log file implicitly calling</span></span><br><span class="line"><span class="comment"># BGREWRITEAOF when the AOF log size grows by the specified percentage.</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># This is how it works: Redis remembers the size of the AOF file after the</span></span><br><span class="line"><span class="comment"># latest rewrite (if no rewrite has happened since the restart, the size of</span></span><br><span class="line"><span class="comment"># the AOF at startup is used).</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># This base size is compared to the current size. If the current size is</span></span><br><span class="line"><span class="comment"># bigger than the specified percentage, the rewrite is triggered. Also</span></span><br><span class="line"><span class="comment"># you need to specify a minimal size for the AOF file to be rewritten, this</span></span><br><span class="line"><span class="comment"># is useful to avoid rewriting the AOF file even if the percentage increase</span></span><br><span class="line"><span class="comment"># is reached but it is still pretty small.</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># Specify a percentage of zero in order to disable the automatic AOF</span></span><br><span class="line"><span class="comment"># rewrite feature.</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># AOF文件大小增长了指定百分比时，自动隐式调用 BGREWRITEAOF 命令进行重写，为 0 则不进行自动重写 </span></span><br><span class="line"><span class="comment"># Redis记录上一次执行AOF重写后的文件大小作为基准，如果启动后没有发生过重写，则使用启动时的AOF文件大小</span></span><br><span class="line"><span class="comment"># 将该基准值与当前文件大小进行比较，如果当前体积超出基准值的指定百分比，将触发重写</span></span><br><span class="line"><span class="attr">auto-aof-rewrite-percentage</span> <span class="string">100</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># 指定要重写的AOF文件的最小体积，避免在文件体积较小时多次重写</span></span><br><span class="line"><span class="attr">auto-aof-rewrite-min-size</span> <span class="string">64mb</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># An AOF file may be found to be truncated at the end during the Redis</span></span><br><span class="line"><span class="comment"># startup process, when the AOF data gets loaded back into memory.</span></span><br><span class="line"><span class="comment"># This may happen when the system where Redis is running</span></span><br><span class="line"><span class="comment"># crashes, especially when an ext4 filesystem is mounted without the</span></span><br><span class="line"><span class="comment"># data=ordered option (however this can&#x27;t happen when Redis itself</span></span><br><span class="line"><span class="comment"># crashes or aborts but the operating system still works correctly).</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># Redis can either exit with an error when this happens, or load as much</span></span><br><span class="line"><span class="comment"># data as possible (the default now) and start if the AOF file is found</span></span><br><span class="line"><span class="comment"># to be truncated at the end. The following option controls this behavior.</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># If aof-load-truncated is set to yes, a truncated AOF file is loaded and</span></span><br><span class="line"><span class="comment"># the Redis server starts emitting a log to inform the user of the event.</span></span><br><span class="line"><span class="comment"># Otherwise if the option is set to no, the server aborts with an error</span></span><br><span class="line"><span class="comment"># and refuses to start. When the option is set to no, the user requires</span></span><br><span class="line"><span class="comment"># to fix the AOF file using the &quot;redis-check-aof&quot; utility before to restart</span></span><br><span class="line"><span class="comment"># the server.</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># Note that if the AOF file will be found to be corrupted in the middle</span></span><br><span class="line"><span class="comment"># the server will still exit with an error. This option only applies when</span></span><br><span class="line"><span class="comment"># Redis will try to read more data from the AOF file but not enough bytes</span></span><br><span class="line"><span class="comment"># will be found.</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># 指定当发生AOF文件末尾截断时，加载文件还是报错退出</span></span><br><span class="line"><span class="comment"># yes 加载文件并并打印日志通知用户</span></span><br><span class="line"><span class="comment"># no 报错并拒绝启动</span></span><br><span class="line"><span class="attr">aof-load-truncated</span> <span class="string">yes</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># When rewriting the AOF file, Redis is able to use an RDB preamble in the</span></span><br><span class="line"><span class="comment"># AOF file for faster rewrites and recoveries. When this option is turned</span></span><br><span class="line"><span class="comment"># on the rewritten AOF file is composed of two different stanzas:</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment">#   [RDB file][AOF tail]</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># When loading Redis recognizes that the AOF file starts with the &quot;REDIS&quot;</span></span><br><span class="line"><span class="comment"># string and loads the prefixed RDB file, and continues loading the AOF</span></span><br><span class="line"><span class="comment"># tail.</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># 开启混合持久化</span></span><br><span class="line"><span class="comment"># 当开启该选项时，触发AOF重写将不再是根据当前内容生成写命令。</span></span><br><span class="line"><span class="comment"># 而是先生成RDB文件写到开头，再将RDB生成期间的发生的增量写命令附加到文件末尾。</span></span><br><span class="line"><span class="attr">aof-use-rdb-preamble</span> <span class="string">yes</span></span><br></pre></td></tr></table></figure>

<h2 id="基本原理-1"><a href="#基本原理-1" class="headerlink" title="基本原理"></a>基本原理</h2><ul>
<li><p>如果服务器开始了 AOF 持久化功能，服务器会优先使用 AOF 文件来还原数据库状态。只有在 AOF 持久化功能处于关闭状态时，服务器才会使用 RDB 文件来还原数据库状态。</p>
</li>
<li><p>每当 Redis 接受到会修改数据集的命令时，就会把命令追加到 AOF 文件里，当你重启 Redis 时，AOF 文件里的命令会被重新执行一次，重建数据。</p>
</li>
<li><p>被写入 AOF 文件中的所有命令都是以 Redis 的命令请求协议格式保存的，因为 Redis 的命令请求协议是纯文本格式，所以我们可以直接打开一个 AOF 文件，观察里面的内容</p>
</li>
<li><p>AOF 重写</p>
<ul>
<li><p>执行方式</p>
<ul>
<li><p>当 AOF 的备份文件过大时，手动输入 <code>bgrewriteaof</code> 命令，进行文件重写</p>
</li>
<li><p>当 AOF 文件的大小超过所设定的阈值时，Redis就会对AOF文件的内容压缩。</p>
</li>
</ul>
</li>
<li><p>重写原理</p>
<ul>
<li>Redis 会 fork 出一条新进程，读取内存中的数据，并重新写到一个临时文件中。并没有读取旧文件。最后替换旧的 aof 文件。</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1 id="同时使用-RDB-和-AOF"><a href="#同时使用-RDB-和-AOF" class="headerlink" title="同时使用 RDB 和 AOF"></a>同时使用 RDB 和 AOF</h1><ul>
<li><p>如果RDB在执行snapshotting操作，那么redis不会执行AOF rewrite; 如果redis再执行AOF rewrite，那么就不会执行RDB snapshotting</p>
</li>
<li><p>如果RDB在执行snapshotting，此时用户执行BGREWRITEAOF命令，那么等RDB快照生成之后，才会去执行AOF rewrite</p>
</li>
<li><p>同时有RDB snapshot文件和AOF日志文件，那么redis重启的时候，会优先使用AOF进行数据恢复，因为其中的日志更完整</p>
</li>
</ul>

        
        
      
    </div>
    <footer class="article-footer">
      
        <div class="article-tag-wrap">
          

          
          


        </div>
      
      <!--  -->
      <!--  -->
    </footer>
  </div>
</article>


  <article id="post-SpringBoot-AOP" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/wei-blog/source/SpringBoot-AOP/">SpringBoot-AOP</a>
    </h1>
  


      </header>
    
    <div class="article-meta">
      
      <!-- <span class="article-date">
  <i class="fa fa-date"></i>
  <time class="dt-published" datetime="2023-02-05T09:20:34.000Z" itemprop="datePublished">2023年02月05日</time>
</span> -->
      
  <div class="article-category">
    <i class="fa fa-classify"></i>
    <a class="article-category-link" href="/wei-blog/categories/SpringBoot/">SpringBoot</a>
  </div>

      
      
<!-- <a href="/wei-blog/source/SpringBoot-AOP/#comments" class="article-comment-link">
  
  <i class="fa fa-commt"></i>
  留言
</a> -->


    </div>
    <div class="e-content article-entry" itemprop="articleBody">
      
        <h1 id="主要概念"><a href="#主要概念" class="headerlink" title="主要概念"></a>主要概念</h1><ul>
<li><p>aspect：切面，切面由切点和通知组成</p>
</li>
<li><p>pointcut：切点，每个类都拥有多个连接点，可以理解为连接点的集合</p>
</li>
<li><p>joinpoint：连接点，程序执行的某个特定位置</p>
</li>
<li><p>weaving：织入，将增强添加到目标类的具体连接点的过程</p>
</li>
<li><p>advice：通知，是织入到目标类连接点上的一段代码</p>
</li>
<li><p>traget：目标对象，通知织入的目标类</p>
</li>
<li><p>aop Proxy：代理对象，既增强后产生的对象</p>
</li>
<li><p>Before advice：前置通知，即在目标方法调用之前执行。无论方法是否遇到异常都执行</p>
</li>
<li><p>After returning advice：后置通知，在目标方法执行后执行，前提是目标方法没有遇到异常，如果有异常则不执行</p>
</li>
<li><p>After throwing advice：异常通知，在目标方法抛出异常时执行，可以获取异常信息</p>
</li>
<li><p>After finally advice：最终通知，在目标方法执行后执行，无论是否是异常执行</p>
</li>
<li><p>Around advice：环绕通知，可以控制目标方法的执行，可以在目标执行全过程中进行执行</p>
</li>
</ul>
<h1 id="相关注解"><a href="#相关注解" class="headerlink" title="相关注解"></a>相关注解</h1><ul>
<li><p>@Component 将当前类注入到Spring容器内</p>
</li>
<li><p>@Aspect ：表明是一个切面类</p>
</li>
<li><p>@Before ：前置通知，在方法执行之前执行</p>
</li>
<li><p>@After ：后置通知，在方法执行之后执行</p>
</li>
<li><p>@AfterRuturning ：返回通知，在方法返回结果之后执行</p>
</li>
<li><p>@AfterThrowing ：异常通知，在方法抛出异常之后执行</p>
</li>
<li><p>@Around ：环绕通知，围绕着方法执行</p>
</li>
<li><p>@Pointcut ：切入点，PointCut（切入点）表达式有很多种，其中execution用于使用切面的连接点。</p>
</li>
</ul>
<p>环绕通知：参数必须为<code>ProceedingJoinPoint</code>，<code>pjp.proceed</code>相应于执行被切面的方法。</p>
<p>返回通知：可以加<code>returning = &quot;XXX&quot;</code>，XXX即为被切入方法的返回值。</p>
<p>异常通知：可以加<code>throwing = &quot;XXX&quot;</code>，供读取异常信息。</p>
<p>返回通知和异常通知只会执行一个，如果产生异常，返回通知就不执行，后置通知一定会执行</p>
<h1 id="使用步骤"><a href="#使用步骤" class="headerlink" title="使用步骤"></a>使用步骤</h1><ol>
<li><p>定义一个切面类Aspect</p>
<p> 在声明的类中增加<code>@Component</code>和<code>@Aspect</code>注解</p>
<p> SpringBoot中要引入spring-boot-starter-aop依赖包</p>
</li>
<li><p>定义切点Pointcut</p>
<p> 定义切点，并定义切点在那些地方执行，采用<code>@Pointcut</code>注解完成</p>
</li>
<li><p>定义Advice通知</p>
<p> 利用通知的5种类型注解<code>@Before</code>、<code>@After</code>、<code>@AfterReturning</code>、<code>@AfterThrowing</code>、<code>@Around</code>来完成在某些切点的增强动作</p>
</li>
</ol>
<h1 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h1><p>Aspect</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Aspect</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WeiAspect</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 切入点：待增强的方法</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Pointcut(&quot;execution(public * cn.xwt.aop.demo.controller.*.*(..))&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">weiPointcut</span><span class="params">()</span>&#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 环绕通知</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> proceedingJoinPoint 连接点</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Throwable 抛出的异常</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Around(&quot;weiPointcut()&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">doAround</span><span class="params">(ProceedingJoinPoint proceedingJoinPoint)</span> <span class="keyword">throws</span> Throwable &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;环绕通知 - 方法执行前&quot;</span>);</span><br><span class="line">        <span class="type">Object</span> <span class="variable">result</span> <span class="operator">=</span> proceedingJoinPoint.proceed();</span><br><span class="line">        System.out.println(<span class="string">&quot;环绕通知 - 方法执行后 - 没有产生异常&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 前置通知</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> joinPoint 连接点</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Throwable 抛出的异常</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Before(&quot;weiPointcut()&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">deBefore</span><span class="params">(JoinPoint joinPoint)</span> <span class="keyword">throws</span> Throwable &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;前置通知 - 一定执行&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 返回通知，没有产生异常时执行</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> returnedObject 返回的数据</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Throwable 抛出的异常</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@AfterReturning(pointcut = &quot;weiPointcut()&quot;, returning = &quot;returnedObject&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">doAfterReturning</span><span class="params">(Object returnedObject)</span> <span class="keyword">throws</span> Throwable &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;后置通知 - 没有产生异常 - 返回值：&quot;</span> + returnedObject);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 异常通知</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> joinPoint 连接点</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> thrownException 方法抛出的异常</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@AfterThrowing(pointcut = &quot;weiPointcut()&quot;, throwing = &quot;thrownException&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">doAfterThrowing</span><span class="params">(JoinPoint joinPoint, Exception thrownException)</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;异常通知 - 产生异常时执行 - 产生异常的方法：&quot;</span> + joinPoint + <span class="string">&quot; - 异常：&quot;</span> + thrownException);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 后置通知</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> joinPoint 连接点</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@After(&quot;weiPointcut()&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">after</span><span class="params">(JoinPoint joinPoint)</span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;后置通知 - 最后且一定执行&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>controller</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DemoController</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/demo&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">demo</span><span class="params">(<span class="meta">@RequestParam(&quot;msg&quot;)</span> String msg)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;方法执行 - &quot;</span> + <span class="string">&quot;msg = &quot;</span> + msg);</span><br><span class="line">        <span class="keyword">return</span> msg;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/demo-exception&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">demoWithException</span><span class="params">(<span class="meta">@RequestParam(&quot;msg&quot;)</span> String msg)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;异常方法执行&quot;</span>);</span><br><span class="line">        <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span> / <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">return</span> msg;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>运行结果</p>
<figure class="highlight tex"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">环绕通知 - 方法执行前</span><br><span class="line">前置通知 - 一定执行</span><br><span class="line">方法执行 - msg = xueweitao</span><br><span class="line">后置通知 - 没有产生异常 - 返回值：xueweitao</span><br><span class="line">后置通知 - 最后且一定执行</span><br><span class="line">环绕通知 - 方法执行后 - 没有产生异常</span><br></pre></td></tr></table></figure>

<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">环绕通知 - 方法执行前</span><br><span class="line">前置通知 - 一定执行</span><br><span class="line">异常方法执行</span><br><span class="line">异常通知 - 产生异常时执行 - 产生异常的方法：execution(String cn.xwt.aop.demo.controller.DemoController.demoWithException(String)) - 异常：java.lang.ArithmeticException: / by zero</span><br><span class="line">后置通知 - 最后且一定执行</span><br><span class="line">。。。。。。。。。异常信息。。。。。。。。。。。。。。。。。。</span><br></pre></td></tr></table></figure>

        
        
      
    </div>
    <footer class="article-footer">
      
        <div class="article-tag-wrap">
          

          
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/wei-blog/tags/AOP/" rel="tag">AOP</a></li></ul>

          


        </div>
      
      <!--  -->
      <!--  -->
    </footer>
  </div>
</article>


  <article id="post-SpringBoot使用图片验证码" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="p-name article-title" href="/wei-blog/source/SpringBoot%E4%BD%BF%E7%94%A8%E5%9B%BE%E7%89%87%E9%AA%8C%E8%AF%81%E7%A0%81/">SpringBoot使用图片验证码</a>
    </h1>
  


      </header>
    
    <div class="article-meta">
      
      <!-- <span class="article-date">
  <i class="fa fa-date"></i>
  <time class="dt-published" datetime="2023-02-05T09:08:01.000Z" itemprop="datePublished">2023年02月05日</time>
</span> -->
      
  <div class="article-category">
    <i class="fa fa-classify"></i>
    <a class="article-category-link" href="/wei-blog/categories/SpringBoot/">SpringBoot</a>
  </div>

      
      
<!-- <a href="/wei-blog/source/SpringBoot%E4%BD%BF%E7%94%A8%E5%9B%BE%E7%89%87%E9%AA%8C%E8%AF%81%E7%A0%81/#comments" class="article-comment-link">
  
  <i class="fa fa-commt"></i>
  留言
</a> -->


    </div>
    <div class="e-content article-entry" itemprop="articleBody">
      
        <h1 id="工具类"><a href="#工具类" class="headerlink" title="工具类"></a>工具类</h1><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">VerifyUtil</span> &#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 验证码字符集</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">char</span>[] CHARS = &#123;</span><br><span class="line">            <span class="string">&#x27;0&#x27;</span>, <span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;2&#x27;</span>, <span class="string">&#x27;3&#x27;</span>, <span class="string">&#x27;4&#x27;</span>, <span class="string">&#x27;5&#x27;</span>, <span class="string">&#x27;6&#x27;</span>, <span class="string">&#x27;7&#x27;</span>, <span class="string">&#x27;8&#x27;</span>, <span class="string">&#x27;9&#x27;</span>,</span><br><span class="line">            <span class="string">&#x27;a&#x27;</span>, <span class="string">&#x27;b&#x27;</span>, <span class="string">&#x27;c&#x27;</span>, <span class="string">&#x27;d&#x27;</span>, <span class="string">&#x27;e&#x27;</span>, <span class="string">&#x27;f&#x27;</span>, <span class="string">&#x27;g&#x27;</span>, <span class="string">&#x27;h&#x27;</span>, <span class="string">&#x27;i&#x27;</span>, <span class="string">&#x27;j&#x27;</span>, <span class="string">&#x27;k&#x27;</span>, <span class="string">&#x27;l&#x27;</span>, <span class="string">&#x27;m&#x27;</span>, <span class="string">&#x27;n&#x27;</span>,</span><br><span class="line">            <span class="string">&#x27;o&#x27;</span>, <span class="string">&#x27;p&#x27;</span>, <span class="string">&#x27;q&#x27;</span>, <span class="string">&#x27;r&#x27;</span>, <span class="string">&#x27;s&#x27;</span>, <span class="string">&#x27;t&#x27;</span>, <span class="string">&#x27;u&#x27;</span>, <span class="string">&#x27;v&#x27;</span>, <span class="string">&#x27;w&#x27;</span>, <span class="string">&#x27;x&#x27;</span>, <span class="string">&#x27;y&#x27;</span>, <span class="string">&#x27;z&#x27;</span>,</span><br><span class="line">            <span class="string">&#x27;A&#x27;</span>, <span class="string">&#x27;B&#x27;</span>, <span class="string">&#x27;C&#x27;</span>, <span class="string">&#x27;D&#x27;</span>, <span class="string">&#x27;E&#x27;</span>, <span class="string">&#x27;F&#x27;</span>, <span class="string">&#x27;G&#x27;</span>, <span class="string">&#x27;H&#x27;</span>, <span class="string">&#x27;I&#x27;</span>, <span class="string">&#x27;J&#x27;</span>, <span class="string">&#x27;K&#x27;</span>, <span class="string">&#x27;L&#x27;</span>, <span class="string">&#x27;M&#x27;</span>, <span class="string">&#x27;N&#x27;</span>,</span><br><span class="line">            <span class="string">&#x27;O&#x27;</span>, <span class="string">&#x27;P&#x27;</span>, <span class="string">&#x27;Q&#x27;</span>, <span class="string">&#x27;R&#x27;</span>, <span class="string">&#x27;S&#x27;</span>, <span class="string">&#x27;T&#x27;</span>, <span class="string">&#x27;U&#x27;</span>, <span class="string">&#x27;V&#x27;</span>, <span class="string">&#x27;W&#x27;</span>, <span class="string">&#x27;X&#x27;</span>, <span class="string">&#x27;Y&#x27;</span>, <span class="string">&#x27;Z&#x27;</span>&#125;;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 生成验证码图片</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> charSize 字符数量</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> lineSize 干扰线数量</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> width 图片宽度</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> height 图片高度</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> fontSize 字体大小</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> HashMap&lt;String, Object&gt; code：验证码 image：图片</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> HashMap&lt;String, Object&gt; <span class="title function_">createImage</span><span class="params">(<span class="type">int</span> charSize, <span class="type">int</span> lineSize, <span class="type">int</span> width, <span class="type">int</span> height, <span class="type">int</span> fontSize)</span> &#123;</span><br><span class="line">        <span class="type">StringBuilder</span> <span class="variable">sb</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StringBuilder</span>();</span><br><span class="line">        <span class="comment">// 1.创建空白图片</span></span><br><span class="line">        <span class="type">BufferedImage</span> <span class="variable">image</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BufferedImage</span>(</span><br><span class="line">                width, height, BufferedImage.TYPE_INT_RGB);</span><br><span class="line">        <span class="comment">// 2.获取图片画笔</span></span><br><span class="line">        <span class="type">Graphics</span> <span class="variable">graphic</span> <span class="operator">=</span> image.getGraphics();</span><br><span class="line">        <span class="comment">// 3.设置画笔颜色</span></span><br><span class="line">        graphic.setColor(Color.LIGHT_GRAY);</span><br><span class="line">        <span class="comment">// 4.绘制矩形背景</span></span><br><span class="line">        graphic.fillRect(<span class="number">0</span>, <span class="number">0</span>, width, height);</span><br><span class="line">        <span class="comment">// 5.画随机字符</span></span><br><span class="line">        <span class="type">Random</span> <span class="variable">ran</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Random</span>();</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt;charSize; i++) &#123;</span><br><span class="line">            <span class="comment">// 取随机字符索引</span></span><br><span class="line">            <span class="type">int</span> <span class="variable">n</span> <span class="operator">=</span> ran.nextInt(CHARS.length);</span><br><span class="line">            <span class="comment">// 设置随机颜色</span></span><br><span class="line">            graphic.setColor(getRandomColor());</span><br><span class="line">            <span class="comment">// 设置字体大小</span></span><br><span class="line">            graphic.setFont(<span class="keyword">new</span> <span class="title class_">Font</span>(</span><br><span class="line">                    <span class="literal">null</span>, Font.BOLD + Font.ITALIC, fontSize));</span><br><span class="line">            <span class="comment">// 画字符</span></span><br><span class="line">            graphic.drawString(</span><br><span class="line">                    CHARS[n] + <span class="string">&quot;&quot;</span>, i * width / charSize, height*<span class="number">2</span>/<span class="number">3</span>);</span><br><span class="line">            <span class="comment">// 记录字符</span></span><br><span class="line">            sb.append(CHARS[n]);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 6.画干扰线</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; lineSize; i++) &#123;</span><br><span class="line">            <span class="comment">// 设置随机颜色</span></span><br><span class="line">            graphic.setColor(getRandomColor());</span><br><span class="line">            <span class="comment">// 随机画线</span></span><br><span class="line">            graphic.drawLine(ran.nextInt(width), ran.nextInt(height),</span><br><span class="line">                    ran.nextInt(width), ran.nextInt(height));</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 7.返回验证码和图片</span></span><br><span class="line">        HashMap&lt;String, Object&gt; map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">        map.put(<span class="string">&quot;code&quot;</span>, sb.toString());</span><br><span class="line">        map.put(<span class="string">&quot;image&quot;</span>, image);</span><br><span class="line">        <span class="keyword">return</span> map;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 随机取色</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> Color <span class="title function_">getRandomColor</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">Random</span> <span class="variable">ran</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Random</span>();</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Color</span>(ran.nextInt(<span class="number">256</span>),</span><br><span class="line">                ran.nextInt(<span class="number">256</span>), ran.nextInt(<span class="number">256</span>));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h1 id="service"><a href="#service" class="headerlink" title="service"></a>service</h1><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 生成验证码和图片</span></span><br><span class="line">HashMap&lt;String, Object&gt; map =</span><br><span class="line">        VerifyUtil.createImage(<span class="number">4</span>, <span class="number">5</span>, <span class="number">100</span>, <span class="number">24</span>, <span class="number">20</span>);</span><br><span class="line"><span class="comment">// 将验证码存入redis</span></span><br><span class="line">redisUtil.setWithSecond(loginId, ((String) map.get(<span class="string">&quot;code&quot;</span>)), <span class="number">3</span> * <span class="number">60</span>);</span><br><span class="line"><span class="keyword">return</span> ((BufferedImage) map.get(<span class="string">&quot;image&quot;</span>));</span><br></pre></td></tr></table></figure>

<h1 id="controller"><a href="#controller" class="headerlink" title="controller"></a>controller</h1><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@GetMapping(&quot;/code&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">getCode</span><span class="params">(HttpServletResponse response, <span class="meta">@RequestParam</span> String loginId)</span>&#123;</span><br><span class="line">    <span class="comment">//将图片输出给浏览器</span></span><br><span class="line">    <span class="type">BufferedImage</span> <span class="variable">image</span> <span class="operator">=</span> userService.getAuthCodeImage(loginId);</span><br><span class="line">    response.setContentType(<span class="string">&quot;image/png&quot;</span>);</span><br><span class="line">    <span class="type">OutputStream</span> <span class="variable">os</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        os = response.getOutputStream();</span><br><span class="line">        ImageIO.write(image, <span class="string">&quot;png&quot;</span>, os);</span><br><span class="line">    &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">assert</span> os != <span class="literal">null</span>;</span><br><span class="line">            os.close();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

        
        
      
    </div>
    <footer class="article-footer">
      
        <div class="article-tag-wrap">
          

          
          


        </div>
      
      <!--  -->
      <!--  -->
    </footer>
  </div>
</article>



    <nav id="page-nav">

<a class="extend prev" rel="prev" href="/wei-blog/">上一页</a><a class="page-number" href="/wei-blog/">1</a><span class="page-number current">2</span><a class="page-number" href="/wei-blog/page/3/">3</a><a class="page-number" href="/wei-blog/page/4/">4</a><a class="extend next" rel="next" href="/wei-blog/page/3/">下一页</a>
</nav>


</section>
        
          <aside id="sidebar">
  
    
  <div class="widget-wrap">
    <h3 class="widget-title"><i class="fa fa-classify"></i> 分类</h3>
    <div class="widget">
      <ul class="category-list"><li class="category-list-item"><a class="category-list-link" href="/wei-blog/categories/Docker/">Docker</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/wei-blog/categories/Java/">Java</a><span class="category-list-count">3</span></li><li class="category-list-item"><a class="category-list-link" href="/wei-blog/categories/RabbitMQ/">RabbitMQ</a><span class="category-list-count">2</span></li><li class="category-list-item"><a class="category-list-link" href="/wei-blog/categories/Redis/">Redis</a><span class="category-list-count">9</span></li><li class="category-list-item"><a class="category-list-link" href="/wei-blog/categories/Spring/">Spring</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/wei-blog/categories/SpringBoot/">SpringBoot</a><span class="category-list-count">13</span><ul class="category-list-child"><li class="category-list-item"><a class="category-list-link" href="/wei-blog/categories/SpringBoot/RabbitMQ/">RabbitMQ</a><span class="category-list-count">1</span></li></ul></li><li class="category-list-item"><a class="category-list-link" href="/wei-blog/categories/SpringSecurity/">SpringSecurity</a><span class="category-list-count">1</span></li></ul>
    </div>
  </div>


  
    
  <div class="widget-wrap">
    <h3 class="widget-title"><i class="fa fa-tag"></i> 标签</h3>
    <div class="widget">
      <ul class="tag-list" itemprop="keywords"><li class="tag-list-item"><a class="tag-list-link" href="/wei-blog/tags/AOP/" rel="tag">AOP</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/wei-blog/tags/JWT/" rel="tag">JWT</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/wei-blog/tags/MyBatis/" rel="tag">MyBatis</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/wei-blog/tags/MyBatisPlus/" rel="tag">MyBatisPlus</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/wei-blog/tags/%E6%8B%A6%E6%88%AA%E5%99%A8/" rel="tag">拦截器</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/wei-blog/tags/%E8%B7%A8%E5%9F%9F/" rel="tag">跨域</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/wei-blog/tags/%E8%BF%90%E8%A1%8C%E9%94%99%E8%AF%AF/" rel="tag">运行错误</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/wei-blog/tags/%E9%80%BB%E8%BE%91/" rel="tag">逻辑</a><span class="tag-list-count">2</span></li></ul>
    </div>
  </div>


  
    
  <div class="widget-wrap">
    <h3 class="widget-title"><i class="fa fa-posts"></i> 最新文章</h3>
    <div class="widget">
      <ul>
        
          <li>
            <a href="/wei-blog/source/%E5%9F%BA%E4%BA%8ERedis%E7%9A%84%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/">基于Redis的分布式锁</a>
          </li>
        
          <li>
            <a href="/wei-blog/source/SpringBoot%E5%AE%9E%E7%8E%B0%E6%8E%A5%E5%8F%A3%E9%99%90%E6%B5%81/">SpringBoot实现接口限流</a>
          </li>
        
          <li>
            <a href="/wei-blog/source/%E7%BC%93%E5%AD%98%E7%A9%BF%E9%80%8F%E3%80%81%E7%BC%93%E5%AD%98%E9%9B%AA%E5%B4%A9%E3%80%81%E7%BC%93%E5%AD%98%E5%87%BB%E7%A9%BF/">(no title)</a>
          </li>
        
          <li>
            <a href="/wei-blog/source/SpringBoot%E6%95%B4%E5%90%88RabbitMQ/">SpringBoot整合RabbitMQ</a>
          </li>
        
          <li>
            <a href="/wei-blog/source/Redis%E4%B9%8BZSet/">(no title)</a>
          </li>
        
      </ul>
    </div>
  </div>


  
</aside>
        
      </div>
      <a id="totop" href="#top"></a>
      <footer id="footer">
  
  <div class="outer">
    <div id="footer-info" class="inner">
      <!-- <p>
        <a href="/wei-blog/sitemap.xml">网站地图</a>
        <span> | </span><a href="/wei-blog/atom.xml">订阅本站</a>
        <span> | </span><a href="/wei-blog/about/">联系博主</a>
      </p> -->
      
      <p>
        <span>Copyright &copy; 2023 xueweitao.</span>
        <!-- <span>Theme by <a href="https://github.com/chaooo/hexo-theme-BlueLake/" target="_blank">BlueLake.</a></span>
        <span>Powered by <a href="https://hexo.io/" target="_blank">Hexo.</a></span> -->
      </p>
    </div>
  </div>
</footer>

    </div>
  </div>
  
<script src="/wei-blog/js/jquery-3.4.1.min.js"></script>


<script src="/wei-blog/js/search.json.js"></script>




<script src="/wei-blog/js/script.js"></script>












  



</body>
</html>